This analysis adapte from Walking the Beat: Mining Seattle’s Police Report Data by Jeff Wong

This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.

1. Including a few packages

if (!require(data.table)) { install.packages('data.table'); require(data.table) }
Loading required package: data.table
data.table 1.9.6  For help type ?data.table or https://github.com/Rdatatable/data.table/wiki
The fastest way to learn (by data.table authors): https://www.datacamp.com/courses/data-analysis-the-data-table-way

Attaching package: ‘data.table’

The following object is masked _by_ ‘.GlobalEnv’:

    .N
if (!require(ggplot2)) { install.packages('ggplot2'); require(ggplot2) }
Loading required package: ggplot2
package ‘ggplot2’ was built under R version 3.3.2
if (!require(reshape2)) { install.packages('reshape2'); require(reshape2) }
Loading required package: reshape2

Attaching package: ‘reshape2’

The following objects are masked from ‘package:data.table’:

    dcast, melt
if (!require(devtools)) { install.packages('devtools'); require(devtools) }
Loading required package: devtools
if (!require(ggmap)) { install.packages('ggmap'); require(ggmap) }
Loading required package: ggmap
Google Maps API Terms of Service: http://developers.google.com/maps/terms.
Please cite ggmap if you use it: see citation('ggmap') for details.

Attaching package: ‘ggmap’

The following object is masked _by_ ‘.GlobalEnv’:

    crime
if (!require(glmnet)) { install.packages('glmnet'); require(glmnet) }
Loading required package: glmnet
package ‘glmnet’ was built under R version 3.3.2Loading required package: Matrix
Loading required package: foreach
foreach: simple, scalable parallel programming from Revolution Analytics
Use Revolution R for scalability, fault tolerance and more.
http://www.revolutionanalytics.com
Loaded glmnet 2.0-13
if (!require(fields)) { install.packages('fields'); require(fields) }
Loading required package: fields
package ‘fields’ was built under R version 3.3.2Loading required package: spam
package ‘spam’ was built under R version 3.3.2Loading required package: dotCall64
Loading required package: grid
failed to assign RegisteredNativeSymbol for transpose to transpose since transpose is already defined in the ‘spam’ namespacefailed to assign RegisteredNativeSymbol for toeplitz to toeplitz since toeplitz is already defined in the ‘spam’ namespaceSpam version 2.1-1 (2017-07-02) is loaded.
Type 'help( Spam)' or 'demo( spam)' for a short introduction 
and overview of this package.
Help for individual functions is also obtained by adding the
suffix '.spam' to the function name, e.g. 'help( chol.spam)'.

Attaching package: ‘spam’

The following objects are masked from ‘package:base’:

    backsolve, forwardsolve

Loading required package: maps
package ‘maps’ was built under R version 3.3.2
if (!require(dplyr)) { install.packages('dplyr'); require(dplyr) }
Loading required package: dplyr
package ‘dplyr’ was built under R version 3.3.2
Attaching package: ‘dplyr’

The following objects are masked from ‘package:data.table’:

    between, last

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
if (!require(lubridate)) { install.packages('lubridate'); require(lubridate) }
Loading required package: lubridate

Attaching package: ‘lubridate’

The following objects are masked from ‘package:data.table’:

    hour, mday, month, quarter, wday, week, yday, year

The following object is masked from ‘package:base’:

    date

2. Loading the data

MGH_LONGITUDE = -122.307987
MGH_LATITUDE = 47.655038
PATH_DATA_ALL = paste(getwd(), "/data/Seattle_Police_Department_911_Incident_Response.csv", sep = "")
PATH_DATA_MGH = paste(getwd(), "/data/Seattle_Police_Department_911_Incident_Response_Near_MGH.csv", sep = "")
PATH_DATA_MGH_TINY = paste(getwd(), "/data/Seattle_Police_Department_911_Incident_Response_Near_MGH_tiny.csv", sep = "")
if(file.exists(PATH_DATA_MGH)){ # switch variable to PATH_DATA_MGH_TINY if there are issues with scale of data
  data <- fread(PATH_DATA_MGH, header=T, sep=",")
} else {
  data_all = fread(PATH_DATA_ALL, header=T, sep=",")
  data_all <- data_all %>% mutate(dist_mgh = sqrt((MGH_LATITUDE-Latitude)^2 + (MGH_LONGITUDE-Longitude)^2)) # Finding crimes closest to MGH
  data_mgh <- filter(data_all, dist_mgh<0.01) #22,110 rows
  data_mgh_tiny <- filter(data_mgh, dist_mgh<0.007) #5,100 rows
}
C function strtod() returned ERANGE for one or more fields. The first was string input '4.94077829302447e-314'. It was read using (double)strtold() as numeric value 4.9407782930244673E-314 (displayed here using %.16E); loss of accuracy likely occurred. This message is designed to tell you exactly what has been done by fread's C code, so you can search yourself online for many references about double precision accuracy and these specific C functions. You may wish to use colClasses to read the column as character instead and then coerce that column using the Rmpfr package for greater accuracy.
#adding columns to the data
data[,at_scene_time_ts := as.POSIXct(strptime(`At Scene Time`, "%m/%d/%Y %I:%M:%S %p"))] #converting time from String to date and time representation (POSIXct)
data[,at_scene_time_hr := hour(ymd_hms(as.character(at_scene_time_ts)))]
data[,at_scene_time_date := as.Date(at_scene_time_ts)]
data[,at_scene_time_week := floor(as.numeric(at_scene_time_date - min(at_scene_time_date, na.rm=T)) / 7) + 1]
data[,event_clearance_ts := as.POSIXct(strptime(`Event Clearance Date`, "%m/%d/%Y %I:%M:%S %p"))]
data[,event_clearance_date := as.Date(event_clearance_ts)]
data[,event_clearance_hr := hour(ymd_hms(as.character(event_clearance_ts)))]
data[,time_until_event_clear := as.numeric(event_clearance_ts - at_scene_time_ts)]
data[,`Initial Type Group` := factor(`Initial Type Group`)]
data[,`Event Clearance Group` := factor(`Event Clearance Group`)]
data[,`Zone/Beat` := factor(`Zone/Beat`)]
data[,LatitudeBin := round(Latitude, 3)]
data[,LongitudeBin := round(Longitude, 3)]
data[,crime_type := ifelse(`Event Clearance Group` %in% crimes.violent, "Violent", ifelse(`Event Clearance Group` %in% crimes.serious, "Serious", "Minor"))]
View(data)

3. Missing Data

Missing data can be a problem. If a large proportion of data is missing, we may end with results that are not representative of the population.

TODO There are several options for date/time (at_scene_time_ts, event_clearance_ts). Figure out what proportion of reports have values. hint: is.na can be helpful

total_reports = nrow(data)
total_reports
[1] 22110
# write code below!

4. Frequency of Crimes by Day of Week

Let’s do a quick sanity check of our data by looking at the number of reports by day of week. Because police services are available every day of the week, we would expect at least some reports occuring each day of the week. Run the code below to verify that.

sanityCheck = data.frame(data[,table(weekdays(event_clearance_date))]); # frequency of crimes by day of week
View(sanityCheck)
sanityCheck$Var1 = factor(sanityCheck$Var1, levels = rev(c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")))
ggplot(sanityCheck,
       aes(x = Var1, y = Freq, fill = as.numeric(Var1) %% 2)) +
  geom_bar(stat = 'identity') +
  coord_flip() +
  theme_bw() + 
  xlab ("Day of Week") + ylab("Count") +
  guides(fill=F)

5. Frequency of Crimes by Time of Day

Perhaps we should have more officers patroling during times of day which are high crime. TODO Plot the number of reported incidents by hour of day and see what suggestions you may make.

hint this code should look similar to the previous block (Frequency of Crimes by Day of Week). challenge: Crime by time of day may vary by weekend days and weekdays. Plot the crime by times of day for weekdays and weekend days separately.

# Your code below!

6. Types of contacts

Not all crime is the same! Let’s see the frequency of each type of crime. We provide a function ggplot.freqtable.1d to generate the plot. Run the code below.

#Uses a frequency table to build a bar chart that is ordered by the counts
ggplot.freqtable.1d = function(x) {
  freq = data.frame(x); colnames(freq)[1] = "label"
  freq.filtered = droplevels(subset(freq, label != "" & !is.na(label)))
  freq.filtered$label = factor(freq.filtered$label,
                               levels = as.character(freq.filtered[order(freq.filtered$Freq, decreasing = F),]$label),
                               ordered = F)
  ggplot(freq.filtered,
         aes(x = label, y = Freq, fill = label)) +
    geom_bar(stat = 'identity') +
    coord_flip() +
    theme_grey()
}
#Ploting frequency of each type of report
ggplot.freqtable.1d(data[,table(`Event Clearance Group`)]) +
  xlab("Contact Type") + ylab("Count") +
  theme_bw() +
  guides(fill=F)

7. Digging deeper in the type of event

There are a lot of “Disturbances” and “Suspicious Circumstances” reported, but what does that mean? The “Event Clearance Description” field may provide more information.

TODO: Using the ggplot.freqtable.1d function from above, generate plots which provide more information on the types of crimes which are labeled as “Disturbances.” hint: Filter the data by Event Clearance Group challenge: Do the same analysis for the “Suspicious Circumstances” group.

8. Heatmap of Crimes

Some places may be more of hotbeds from crime. Run the code below to figure out which areas have more reported crimes.

ZOOM <- 15 # number between 1 and 21
seattle = get_map(location = c(lon = MGH_LONGITUDE, lat = MGH_LATITUDE), zoom = ZOOM, maptype = 'roadmap')
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=47.655038,-122.307987&zoom=15&size=640x640&scale=2&maptype=roadmap&language=en-EN&sensor=false
data.filtered = data[!is.na(time_until_event_clear) & time_until_event_clear > 0,
                     list(count = .N),
                     list(LongitudeBin, LatitudeBin)]
# If you run into an error about GeomRasterAnn being built on an incompatible version of ggproto, you must reinstall ggmap. Use this command: install.packages("ggmap", type = "source")
ggmap(seattle) +
  geom_point(data = data.filtered[count > 10],
             mapping = aes(x = LongitudeBin, y = LatitudeBin, size = log10(count), alpha = log10(count)), color = 'blue') +
  theme_grey() +
  theme(legend.position="top") +
  xlab("Longitude") + ylab("Latitude") + labs(title = "Heatmap of Crimes")

9: Responding to casualties

Crimes where people are injured require a fast response. Run this code to look at a heatmap of the time it takes to clear crimes involving injury. TODO: Change the type of crime to determine if clearance time varies by crime.

crime = 'PERSON DOWN/INJURY'
data.filtered = data[!is.na(time_until_event_clear) & time_until_event_clear > 0]
data.filtered.contact = data.filtered[`Event Clearance Group` == crime]
time_until_event_clear.q01 = quantile(data.filtered.contact$time_until_event_clear, .01); time_until_event_clear.q99 = quantile(data.filtered.contact$time_until_event_clear, .99)
data.filtered.contact = data.filtered.contact[time_until_event_clear > time_until_event_clear.q01 & time_until_event_clear < time_until_event_clear.q99]
data.filtered.contact2 = data.filtered.contact[,list(Hours = mean(time_until_event_clear / 3600), count = .N), list(Longitude = LongitudeBin, Latitude = LatitudeBin)]
ggmap(seattle) +
  geom_point(data = data.filtered.contact2,
             mapping = aes(x = Longitude, y = Latitude, color = Hours, alpha = Hours, size = count)) +
  scale_colour_gradientn(colours=c("blue", "red")) +
  scale_alpha(range = c(0.2, 0.7)) +
  scale_size_continuous(range = c(5,20)) +
  guides(alpha=F,size=F) +
  theme_grey() +
  theme(legend.position="top") +
  xlab("Lon") + ylab("Lat") + 
  labs(title = sprintf("Time Until %s Cleared", crime))

LS0tCnRpdGxlOiAiVW5kZXJzdGFuZGluZyA5MTEgY2FsbHMgYXJvdW5kIFVXIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCl9UaGlzIGFuYWx5c2lzIGFkYXB0ZSBmcm9tIFtXYWxraW5nIHRoZSBCZWF0OiBNaW5pbmcgU2VhdHRsZeKAmXMgUG9saWNlIFJlcG9ydCBEYXRhXShodHRwczovL3d3dy5iYXllc2ltcGFjdC5vcmcvc3Rvcmllcy8/bmFtZT13YWxraW5nLXRoZS1iZWF0LW1pbmluZy1zZWF0dGxlcy1wb2xpY2UtcmVwb3J0LWRhdGEpIGJ5IEplZmYgV29uZ18KClRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuIAoKVHJ5IGV4ZWN1dGluZyB0aGlzIGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkNtZCtTaGlmdCtFbnRlciouIAoKIyAxLiBJbmNsdWRpbmcgYSBmZXcgcGFja2FnZXMKYGBge3J9CmlmICghcmVxdWlyZShkYXRhLnRhYmxlKSkgeyBpbnN0YWxsLnBhY2thZ2VzKCdkYXRhLnRhYmxlJyk7IHJlcXVpcmUoZGF0YS50YWJsZSkgfQppZiAoIXJlcXVpcmUoZ2dwbG90MikpIHsgaW5zdGFsbC5wYWNrYWdlcygnZ2dwbG90MicpOyByZXF1aXJlKGdncGxvdDIpIH0KaWYgKCFyZXF1aXJlKHJlc2hhcGUyKSkgeyBpbnN0YWxsLnBhY2thZ2VzKCdyZXNoYXBlMicpOyByZXF1aXJlKHJlc2hhcGUyKSB9CmlmICghcmVxdWlyZShkZXZ0b29scykpIHsgaW5zdGFsbC5wYWNrYWdlcygnZGV2dG9vbHMnKTsgcmVxdWlyZShkZXZ0b29scykgfQppZiAoIXJlcXVpcmUoZ2dtYXApKSB7IGluc3RhbGwucGFja2FnZXMoJ2dnbWFwJyk7IHJlcXVpcmUoZ2dtYXApIH0KaWYgKCFyZXF1aXJlKGdsbW5ldCkpIHsgaW5zdGFsbC5wYWNrYWdlcygnZ2xtbmV0Jyk7IHJlcXVpcmUoZ2xtbmV0KSB9CmlmICghcmVxdWlyZShmaWVsZHMpKSB7IGluc3RhbGwucGFja2FnZXMoJ2ZpZWxkcycpOyByZXF1aXJlKGZpZWxkcykgfQppZiAoIXJlcXVpcmUoZHBseXIpKSB7IGluc3RhbGwucGFja2FnZXMoJ2RwbHlyJyk7IHJlcXVpcmUoZHBseXIpIH0KaWYgKCFyZXF1aXJlKGx1YnJpZGF0ZSkpIHsgaW5zdGFsbC5wYWNrYWdlcygnbHVicmlkYXRlJyk7IHJlcXVpcmUobHVicmlkYXRlKSB9CmBgYAoKIyAyLiBMb2FkaW5nIHRoZSBkYXRhCmBgYHtyfQpNR0hfTE9OR0lUVURFID0gLTEyMi4zMDc5ODcKTUdIX0xBVElUVURFID0gNDcuNjU1MDM4CgpQQVRIX0RBVEFfQUxMID0gcGFzdGUoZ2V0d2QoKSwgIi9kYXRhL1NlYXR0bGVfUG9saWNlX0RlcGFydG1lbnRfOTExX0luY2lkZW50X1Jlc3BvbnNlLmNzdiIsIHNlcCA9ICIiKQpQQVRIX0RBVEFfTUdIID0gcGFzdGUoZ2V0d2QoKSwgIi9kYXRhL1NlYXR0bGVfUG9saWNlX0RlcGFydG1lbnRfOTExX0luY2lkZW50X1Jlc3BvbnNlX05lYXJfTUdILmNzdiIsIHNlcCA9ICIiKQpQQVRIX0RBVEFfTUdIX1RJTlkgPSBwYXN0ZShnZXR3ZCgpLCAiL2RhdGEvU2VhdHRsZV9Qb2xpY2VfRGVwYXJ0bWVudF85MTFfSW5jaWRlbnRfUmVzcG9uc2VfTmVhcl9NR0hfdGlueS5jc3YiLCBzZXAgPSAiIikKCmlmKGZpbGUuZXhpc3RzKFBBVEhfREFUQV9NR0gpKXsgIyBzd2l0Y2ggdmFyaWFibGUgdG8gUEFUSF9EQVRBX01HSF9USU5ZIGlmIHRoZXJlIGFyZSBpc3N1ZXMgd2l0aCBzY2FsZSBvZiBkYXRhCiAgZGF0YSA8LSBmcmVhZChQQVRIX0RBVEFfTUdILCBoZWFkZXI9VCwgc2VwPSIsIikKfSBlbHNlIHsKICBkYXRhX2FsbCA9IGZyZWFkKFBBVEhfREFUQV9BTEwsIGhlYWRlcj1ULCBzZXA9IiwiKQogIGRhdGFfYWxsIDwtIGRhdGFfYWxsICU+JSBtdXRhdGUoZGlzdF9tZ2ggPSBzcXJ0KChNR0hfTEFUSVRVREUtTGF0aXR1ZGUpXjIgKyAoTUdIX0xPTkdJVFVERS1Mb25naXR1ZGUpXjIpKSAjIEZpbmRpbmcgY3JpbWVzIGNsb3Nlc3QgdG8gTUdICiAgZGF0YV9tZ2ggPC0gZmlsdGVyKGRhdGFfYWxsLCBkaXN0X21naDwwLjAxKSAjMjIsMTEwIHJvd3MKICBkYXRhX21naF90aW55IDwtIGZpbHRlcihkYXRhX21naCwgZGlzdF9tZ2g8MC4wMDcpICM1LDEwMCByb3dzCn0KCiNhZGRpbmcgY29sdW1ucyB0byB0aGUgZGF0YQpkYXRhWyxhdF9zY2VuZV90aW1lX3RzIDo9IGFzLlBPU0lYY3Qoc3RycHRpbWUoYEF0IFNjZW5lIFRpbWVgLCAiJW0vJWQvJVkgJUk6JU06JVMgJXAiKSldICNjb252ZXJ0aW5nIHRpbWUgZnJvbSBTdHJpbmcgdG8gZGF0ZSBhbmQgdGltZSByZXByZXNlbnRhdGlvbiAoUE9TSVhjdCkKZGF0YVssYXRfc2NlbmVfdGltZV9ociA6PSBob3VyKHltZF9obXMoYXMuY2hhcmFjdGVyKGF0X3NjZW5lX3RpbWVfdHMpKSldCmRhdGFbLGF0X3NjZW5lX3RpbWVfZGF0ZSA6PSBhcy5EYXRlKGF0X3NjZW5lX3RpbWVfdHMpXQpkYXRhWyxhdF9zY2VuZV90aW1lX3dlZWsgOj0gZmxvb3IoYXMubnVtZXJpYyhhdF9zY2VuZV90aW1lX2RhdGUgLSBtaW4oYXRfc2NlbmVfdGltZV9kYXRlLCBuYS5ybT1UKSkgLyA3KSArIDFdCmRhdGFbLGV2ZW50X2NsZWFyYW5jZV90cyA6PSBhcy5QT1NJWGN0KHN0cnB0aW1lKGBFdmVudCBDbGVhcmFuY2UgRGF0ZWAsICIlbS8lZC8lWSAlSTolTTolUyAlcCIpKV0KZGF0YVssZXZlbnRfY2xlYXJhbmNlX2RhdGUgOj0gYXMuRGF0ZShldmVudF9jbGVhcmFuY2VfdHMpXQpkYXRhWyxldmVudF9jbGVhcmFuY2VfaHIgOj0gaG91cih5bWRfaG1zKGFzLmNoYXJhY3RlcihldmVudF9jbGVhcmFuY2VfdHMpKSldCmRhdGFbLHRpbWVfdW50aWxfZXZlbnRfY2xlYXIgOj0gYXMubnVtZXJpYyhldmVudF9jbGVhcmFuY2VfdHMgLSBhdF9zY2VuZV90aW1lX3RzKV0KZGF0YVssYEluaXRpYWwgVHlwZSBHcm91cGAgOj0gZmFjdG9yKGBJbml0aWFsIFR5cGUgR3JvdXBgKV0KZGF0YVssYEV2ZW50IENsZWFyYW5jZSBHcm91cGAgOj0gZmFjdG9yKGBFdmVudCBDbGVhcmFuY2UgR3JvdXBgKV0KZGF0YVssYFpvbmUvQmVhdGAgOj0gZmFjdG9yKGBab25lL0JlYXRgKV0KZGF0YVssTGF0aXR1ZGVCaW4gOj0gcm91bmQoTGF0aXR1ZGUsIDMpXQpkYXRhWyxMb25naXR1ZGVCaW4gOj0gcm91bmQoTG9uZ2l0dWRlLCAzKV0KZGF0YVssY3JpbWVfdHlwZSA6PSBpZmVsc2UoYEV2ZW50IENsZWFyYW5jZSBHcm91cGAgJWluJSBjcmltZXMudmlvbGVudCwgIlZpb2xlbnQiLCBpZmVsc2UoYEV2ZW50IENsZWFyYW5jZSBHcm91cGAgJWluJSBjcmltZXMuc2VyaW91cywgIlNlcmlvdXMiLCAiTWlub3IiKSldCgpWaWV3KGRhdGEpCmBgYAoKIyAzLiBNaXNzaW5nIERhdGEKTWlzc2luZyBkYXRhIGNhbiBiZSBhIHByb2JsZW0uIElmIGEgbGFyZ2UgcHJvcG9ydGlvbiBvZiBkYXRhIGlzIG1pc3NpbmcsIHdlIG1heSBlbmQgd2l0aCByZXN1bHRzIHRoYXQgYXJlIG5vdCByZXByZXNlbnRhdGl2ZSBvZiB0aGUgcG9wdWxhdGlvbi4KCipUT0RPKiBUaGVyZSBhcmUgc2V2ZXJhbCBvcHRpb25zIGZvciBkYXRlL3RpbWUgKGF0X3NjZW5lX3RpbWVfdHMsIGV2ZW50X2NsZWFyYW5jZV90cykuIEZpZ3VyZSBvdXQgd2hhdCBwcm9wb3J0aW9uIG9mIHJlcG9ydHMgaGF2ZSB2YWx1ZXMuIApfaGludF86IGlzLm5hIGNhbiBiZSBoZWxwZnVsCmBgYHtyfQp0b3RhbF9yZXBvcnRzID0gbnJvdyhkYXRhKQp0b3RhbF9yZXBvcnRzCgojIHdyaXRlIGNvZGUgYmVsb3chCmBgYAoKCiM0LiBGcmVxdWVuY3kgb2YgQ3JpbWVzIGJ5IERheSBvZiBXZWVrCkxldCdzIGRvIGEgcXVpY2sgc2FuaXR5IGNoZWNrIG9mIG91ciBkYXRhIGJ5IGxvb2tpbmcgYXQgdGhlIG51bWJlciBvZiByZXBvcnRzIGJ5IGRheSBvZiB3ZWVrLiBCZWNhdXNlIHBvbGljZSBzZXJ2aWNlcyBhcmUgYXZhaWxhYmxlIGV2ZXJ5IGRheSBvZiB0aGUgd2Vlaywgd2Ugd291bGQgZXhwZWN0IGF0IGxlYXN0IHNvbWUgcmVwb3J0cyBvY2N1cmluZyBlYWNoIGRheSBvZiB0aGUgd2Vlay4gUnVuIHRoZSBjb2RlIGJlbG93IHRvIHZlcmlmeSB0aGF0LgpgYGB7cn0Kc2FuaXR5Q2hlY2sgPSBkYXRhLmZyYW1lKGRhdGFbLHRhYmxlKHdlZWtkYXlzKGV2ZW50X2NsZWFyYW5jZV9kYXRlKSldKTsgIyBmcmVxdWVuY3kgb2YgY3JpbWVzIGJ5IGRheSBvZiB3ZWVrClZpZXcoc2FuaXR5Q2hlY2spCgpzYW5pdHlDaGVjayRWYXIxID0gZmFjdG9yKHNhbml0eUNoZWNrJFZhcjEsIGxldmVscyA9IHJldihjKCJTdW5kYXkiLCAiTW9uZGF5IiwgIlR1ZXNkYXkiLCAiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgIkZyaWRheSIsICJTYXR1cmRheSIpKSkKCmdncGxvdChzYW5pdHlDaGVjaywKICAgICAgIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEsIGZpbGwgPSBhcy5udW1lcmljKFZhcjEpICUlIDIpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lX2J3KCkgKyAKICB4bGFiICgiRGF5IG9mIFdlZWsiKSArIHlsYWIoIkNvdW50IikgKwogIGd1aWRlcyhmaWxsPUYpCmBgYAoKCiM1LiBGcmVxdWVuY3kgb2YgQ3JpbWVzIGJ5IFRpbWUgb2YgRGF5ClBlcmhhcHMgd2Ugc2hvdWxkIGhhdmUgbW9yZSBvZmZpY2VycyBwYXRyb2xpbmcgZHVyaW5nIHRpbWVzIG9mIGRheSB3aGljaCBhcmUgaGlnaCBjcmltZS4gCipUT0RPKiBQbG90IHRoZSBudW1iZXIgb2YgcmVwb3J0ZWQgaW5jaWRlbnRzIGJ5IGhvdXIgb2YgZGF5IGFuZCBzZWUgd2hhdCBzdWdnZXN0aW9ucyB5b3UgbWF5IG1ha2UuIAoKX2hpbnRfIHRoaXMgY29kZSBzaG91bGQgbG9vayBzaW1pbGFyIHRvIHRoZSBwcmV2aW91cyBibG9jayAoRnJlcXVlbmN5IG9mIENyaW1lcyBieSBEYXkgb2YgV2VlaykuCl9jaGFsbGVuZ2VfOiBDcmltZSBieSB0aW1lIG9mIGRheSBtYXkgdmFyeSBieSB3ZWVrZW5kIGRheXMgYW5kIHdlZWtkYXlzLiBQbG90IHRoZSBjcmltZSBieSB0aW1lcyBvZiBkYXkgZm9yIHdlZWtkYXlzIGFuZCB3ZWVrZW5kIGRheXMgc2VwYXJhdGVseS4KYGBge3J9CiMgWW91ciBjb2RlIGJlbG93IQpgYGAKCiM2LiBUeXBlcyBvZiBjb250YWN0cwpOb3QgYWxsIGNyaW1lIGlzIHRoZSBzYW1lISBMZXQncyBzZWUgdGhlIGZyZXF1ZW5jeSBvZiBlYWNoIHR5cGUgb2YgY3JpbWUuIFdlIHByb3ZpZGUgYSBmdW5jdGlvbiBnZ3Bsb3QuZnJlcXRhYmxlLjFkIHRvIGdlbmVyYXRlIHRoZSBwbG90LiBSdW4gdGhlIGNvZGUgYmVsb3cuCmBgYHtyfQojVXNlcyBhIGZyZXF1ZW5jeSB0YWJsZSB0byBidWlsZCBhIGJhciBjaGFydCB0aGF0IGlzIG9yZGVyZWQgYnkgdGhlIGNvdW50cwpnZ3Bsb3QuZnJlcXRhYmxlLjFkID0gZnVuY3Rpb24oeCkgewogIGZyZXEgPSBkYXRhLmZyYW1lKHgpOyBjb2xuYW1lcyhmcmVxKVsxXSA9ICJsYWJlbCIKICBmcmVxLmZpbHRlcmVkID0gZHJvcGxldmVscyhzdWJzZXQoZnJlcSwgbGFiZWwgIT0gIiIgJiAhaXMubmEobGFiZWwpKSkKICBmcmVxLmZpbHRlcmVkJGxhYmVsID0gZmFjdG9yKGZyZXEuZmlsdGVyZWQkbGFiZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBhcy5jaGFyYWN0ZXIoZnJlcS5maWx0ZXJlZFtvcmRlcihmcmVxLmZpbHRlcmVkJEZyZXEsIGRlY3JlYXNpbmcgPSBGKSxdJGxhYmVsKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBGKQogIGdncGxvdChmcmVxLmZpbHRlcmVkLAogICAgICAgICBhZXMoeCA9IGxhYmVsLCB5ID0gRnJlcSwgZmlsbCA9IGxhYmVsKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICB0aGVtZV9ncmV5KCkKfQoKI1Bsb3RpbmcgZnJlcXVlbmN5IG9mIGVhY2ggdHlwZSBvZiByZXBvcnQKZ2dwbG90LmZyZXF0YWJsZS4xZChkYXRhWyx0YWJsZShgRXZlbnQgQ2xlYXJhbmNlIEdyb3VwYCldKSArCiAgeGxhYigiQ29udGFjdCBUeXBlIikgKyB5bGFiKCJDb3VudCIpICsKICB0aGVtZV9idygpICsKCiAgZ3VpZGVzKGZpbGw9RikKYGBgCgojNy4gRGlnZ2luZyBkZWVwZXIgaW4gdGhlIHR5cGUgb2YgZXZlbnQKVGhlcmUgYXJlIGEgbG90IG9mICJEaXN0dXJiYW5jZXMiIGFuZCAiU3VzcGljaW91cyBDaXJjdW1zdGFuY2VzIiByZXBvcnRlZCwgYnV0IHdoYXQgZG9lcyB0aGF0IG1lYW4/IFRoZSAiRXZlbnQgQ2xlYXJhbmNlIERlc2NyaXB0aW9uIiBmaWVsZCBtYXkgcHJvdmlkZSBtb3JlIGluZm9ybWF0aW9uLiAKCipUT0RPKjogVXNpbmcgdGhlIGdncGxvdC5mcmVxdGFibGUuMWQgZnVuY3Rpb24gZnJvbSBhYm92ZSwgZ2VuZXJhdGUgcGxvdHMgd2hpY2ggcHJvdmlkZSBtb3JlIGluZm9ybWF0aW9uIG9uIHRoZSB0eXBlcyBvZiBjcmltZXMgd2hpY2ggYXJlIGxhYmVsZWQgYXMgIkRpc3R1cmJhbmNlcy4iCl9oaW50XzogRmlsdGVyIHRoZSBkYXRhIGJ5IGBFdmVudCBDbGVhcmFuY2UgR3JvdXBgCl9jaGFsbGVuZ2VfOiBEbyB0aGUgc2FtZSBhbmFseXNpcyBmb3IgdGhlICJTdXNwaWNpb3VzIENpcmN1bXN0YW5jZXMiIGdyb3VwLgpgYGB7cn0KI1RPRE8KYGBgCgojOC4gSGVhdG1hcCBvZiBDcmltZXMKU29tZSBwbGFjZXMgbWF5IGJlIG1vcmUgb2YgaG90YmVkcyBmcm9tIGNyaW1lLiBSdW4gdGhlIGNvZGUgYmVsb3cgdG8gZmlndXJlIG91dCB3aGljaCBhcmVhcyBoYXZlIG1vcmUgcmVwb3J0ZWQgY3JpbWVzLgpgYGB7cn0KWk9PTSA8LSAxNSAjIG51bWJlciBiZXR3ZWVuIDEgYW5kIDIxCgpzZWF0dGxlID0gZ2V0X21hcChsb2NhdGlvbiA9IGMobG9uID0gTUdIX0xPTkdJVFVERSwgbGF0ID0gTUdIX0xBVElUVURFKSwgem9vbSA9IFpPT00sIG1hcHR5cGUgPSAncm9hZG1hcCcpCmRhdGEuZmlsdGVyZWQgPSBkYXRhWyFpcy5uYSh0aW1lX3VudGlsX2V2ZW50X2NsZWFyKSAmIHRpbWVfdW50aWxfZXZlbnRfY2xlYXIgPiAwLAogICAgICAgICAgICAgICAgICAgICBsaXN0KGNvdW50ID0gLk4pLAogICAgICAgICAgICAgICAgICAgICBsaXN0KExvbmdpdHVkZUJpbiwgTGF0aXR1ZGVCaW4pXQoKIyBJZiB5b3UgcnVuIGludG8gYW4gZXJyb3IgYWJvdXQgR2VvbVJhc3RlckFubiBiZWluZyBidWlsdCBvbiBhbiBpbmNvbXBhdGlibGUgdmVyc2lvbiBvZiBnZ3Byb3RvLCB5b3UgbXVzdCByZWluc3RhbGwgZ2dtYXAuIFVzZSB0aGlzIGNvbW1hbmQ6IGluc3RhbGwucGFja2FnZXMoImdnbWFwIiwgdHlwZSA9ICJzb3VyY2UiKQpnZ21hcChzZWF0dGxlKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0YS5maWx0ZXJlZFtjb3VudCA+IDEwXSwKICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IExvbmdpdHVkZUJpbiwgeSA9IExhdGl0dWRlQmluLCBzaXplID0gbG9nMTAoY291bnQpLCBhbHBoYSA9IGxvZzEwKGNvdW50KSksIGNvbG9yID0gJ2JsdWUnKSArCiAgdGhlbWVfZ3JleSgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIpICsKICB4bGFiKCJMb25naXR1ZGUiKSArIHlsYWIoIkxhdGl0dWRlIikgKyBsYWJzKHRpdGxlID0gIkhlYXRtYXAgb2YgQ3JpbWVzIikKCmBgYAoKIzk6IFJlc3BvbmRpbmcgdG8gY2FzdWFsdGllcwpDcmltZXMgd2hlcmUgcGVvcGxlIGFyZSBpbmp1cmVkIHJlcXVpcmUgYSBmYXN0IHJlc3BvbnNlLiBSdW4gdGhpcyBjb2RlIHRvIGxvb2sgYXQgYSBoZWF0bWFwIG9mIHRoZSB0aW1lIGl0IHRha2VzIHRvIGNsZWFyIGNyaW1lcyBpbnZvbHZpbmcgaW5qdXJ5LgoqVE9ETyo6IENoYW5nZSB0aGUgdHlwZSBvZiBjcmltZSB0byBkZXRlcm1pbmUgaWYgY2xlYXJhbmNlIHRpbWUgdmFyaWVzIGJ5IGNyaW1lLgpgYGB7cn0KY3JpbWUgPSAnUEVSU09OIERPV04vSU5KVVJZJwpkYXRhLmZpbHRlcmVkID0gZGF0YVshaXMubmEodGltZV91bnRpbF9ldmVudF9jbGVhcikgJiB0aW1lX3VudGlsX2V2ZW50X2NsZWFyID4gMF0KZGF0YS5maWx0ZXJlZC5jb250YWN0ID0gZGF0YS5maWx0ZXJlZFtgRXZlbnQgQ2xlYXJhbmNlIEdyb3VwYCA9PSBjcmltZV0KdGltZV91bnRpbF9ldmVudF9jbGVhci5xMDEgPSBxdWFudGlsZShkYXRhLmZpbHRlcmVkLmNvbnRhY3QkdGltZV91bnRpbF9ldmVudF9jbGVhciwgLjAxKTsgdGltZV91bnRpbF9ldmVudF9jbGVhci5xOTkgPSBxdWFudGlsZShkYXRhLmZpbHRlcmVkLmNvbnRhY3QkdGltZV91bnRpbF9ldmVudF9jbGVhciwgLjk5KQpkYXRhLmZpbHRlcmVkLmNvbnRhY3QgPSBkYXRhLmZpbHRlcmVkLmNvbnRhY3RbdGltZV91bnRpbF9ldmVudF9jbGVhciA+IHRpbWVfdW50aWxfZXZlbnRfY2xlYXIucTAxICYgdGltZV91bnRpbF9ldmVudF9jbGVhciA8IHRpbWVfdW50aWxfZXZlbnRfY2xlYXIucTk5XQpkYXRhLmZpbHRlcmVkLmNvbnRhY3QyID0gZGF0YS5maWx0ZXJlZC5jb250YWN0WyxsaXN0KEhvdXJzID0gbWVhbih0aW1lX3VudGlsX2V2ZW50X2NsZWFyIC8gMzYwMCksIGNvdW50ID0gLk4pLCBsaXN0KExvbmdpdHVkZSA9IExvbmdpdHVkZUJpbiwgTGF0aXR1ZGUgPSBMYXRpdHVkZUJpbildCgpnZ21hcChzZWF0dGxlKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0YS5maWx0ZXJlZC5jb250YWN0MiwKICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IExvbmdpdHVkZSwgeSA9IExhdGl0dWRlLCBjb2xvciA9IEhvdXJzLCBhbHBoYSA9IEhvdXJzLCBzaXplID0gY291bnQpKSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50bihjb2xvdXJzPWMoImJsdWUiLCAicmVkIikpICsKICBzY2FsZV9hbHBoYShyYW5nZSA9IGMoMC4yLCAwLjcpKSArCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYyg1LDIwKSkgKwogIGd1aWRlcyhhbHBoYT1GLHNpemU9RikgKwogIHRoZW1lX2dyZXkoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSArCiAgeGxhYigiTG9uIikgKyB5bGFiKCJMYXQiKSArIAogIGxhYnModGl0bGUgPSBzcHJpbnRmKCJUaW1lIFVudGlsICVzIENsZWFyZWQiLCBjcmltZSkpCmBgYAoK